#ifndef Client_h__
#define Client_h__

#include "../Common/Common.h"
#include <boost/asio.hpp>
#include "Connection.h"
#include "ConnectionListener.h"
#include "RpcConnection.h"
#include "../Common/Timer.h"
#include "ConnectionManager.h"

class Client : public ConnectionClosedListener,
			private  boost::noncopyable
{
public:
	Client(void);
	~Client(void);

	void run();
	void stop();

	template<typename T>
	bool connect(const std::string& address, const std::string& port, T conn_template);

	template<typename T>
	void asyncConnect(const std::string& address, const std::string& port, T conn_template);

	boost::asio::io_service& getIoService()
	{
		return io_service_;
	}

	virtual void onConnClosed(ConnectionPtr c);

protected:
	void handleConnect(ConnectionPtr c, const boost::system::error_code& error);

	void handleStop();

	void onUpdate(const boost::system::error_code& error);
	void doUpdate();

private:
	boost::asio::io_service io_service_;
	boost::asio::deadline_timer update_timer_;

	volatile bool stopped_;

	std::string address_;
	std::string port_;

	uint32 last_update_time;
	/// The connection manager which owns all live connections.
	ConnectionManager connection_manager_;
};

template<typename T>
bool Client::connect(const std::string& address, const std::string& port, T conn_template)
{
	try
	{
		address_ = address;
		port_ = port;
		// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
		boost::asio::ip::tcp::resolver resolver(io_service_);
		boost::asio::ip::tcp::resolver::query query(address, port);
		boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);

		conn_template->socket().connect(endpoint);
		connection_manager_.start(conn_template);
	}
	catch (...)
	{
		return false;
	}
	return true;
}

template<typename T>
void Client::asyncConnect(const std::string& address, const std::string& port, T conn_template)
{
	address_ = address;
	port_ = port;

	// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
	boost::asio::ip::tcp::resolver resolver(io_service_);
	boost::asio::ip::tcp::resolver::query query(address, port);
	boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);

	conn_template->socket().async_connect(endpoint,
		boost::bind(&Client::handleConnect,this, conn_template, boost::asio::placeholders::error)
		);
}

#endif // Client_h__

